Appearance
Submitted 2026-09-13 — all 2 sample case(s) passed.
cpp
// P1792 [国家集训队] 种树
// 贪心(反悔) + 大根堆 + 双向循环链表
// 环上选 m 个互不相邻的位置使权值和最大:先取堆顶(局部最优),
// 再把它的左右两点合并成一个权值为 A[l]+A[r]-A[i] 的新点放回堆中,
// 相当于"反悔":若这个新点之后被选中,就等价于放弃 i、改选左右两点。
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int val[N]; // 每个点的美观度(合并后会被覆盖为反悔权值)
int pre[N], nxt[N]; // 双向循环链表
bool vis[N]; // 是否已被合并删除
struct Node {
int v, id;
bool operator<(const Node &o) const { return v < o.v; }
};
priority_queue<Node> q;
int main() {
int n, m;
if (scanf("%d %d", &n, &m) != 2) return 0;
for (int i = 1; i <= n; ++i) scanf("%d", &val[i]);
// 最多能种 floor(n/2) 棵(隔一个种一个),否则无解
if (m > n / 2) {
printf("Error!\n");
return 0;
}
if (m == 0) {
printf("0\n");
return 0;
}
for (int i = 1; i <= n; ++i) {
pre[i] = (i == 1) ? n : i - 1;
nxt[i] = (i == n) ? 1 : i + 1;
q.push({val[i], i});
}
long long ans = 0;
for (int t = 0; t < m; ++t) {
while (vis[q.top().id]) q.pop(); // 丢掉已删除的惰性结点
Node cur = q.top();
q.pop();
int id = cur.id, l = pre[id], r = nxt[id];
ans += cur.v;
// 删除左右两点,用 id 占据它们之间的位置
vis[l] = vis[r] = true;
val[id] = val[l] + val[r] - val[id];
int ll = pre[l], rr = nxt[r];
pre[id] = ll;
nxt[ll] = id;
nxt[id] = rr;
pre[rr] = id;
q.push({val[id], id});
}
printf("%lld\n", ans);
return 0;
}